/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.bff.gaia.runtime.operators;

import com.bff.gaia.api.common.ExecutionConfig;
import com.bff.gaia.api.common.functions.AbstractRichFunction;
import com.bff.gaia.core.fs.FSDataOutputStream;
import com.bff.gaia.core.fs.FileSystem;
import com.bff.gaia.core.fs.Path;
import com.bff.gaia.metrics.Counter;
import com.bff.gaia.runtime.operators.util.metrics.CountingCollector;
import com.bff.gaia.util.Collector;
import com.bff.gaia.util.MutableObjectIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;

/**
 * A driver that does nothing but forward data from its input to its output.
 * 
 * @param <T> The data type.
 */
public class NoOpDriver<T> implements Driver<AbstractRichFunction, T> {

	private static final Logger LOG = LoggerFactory.getLogger(NoOpDriver.class);

	private TaskContext<AbstractRichFunction, T> taskContext;
	
	private volatile boolean running;

	private boolean objectReuseEnabled = false;

	//task2.3_iteration
	private List<T> saveData;
	//end

	@Override
	public void setup(TaskContext<AbstractRichFunction, T> context) {
		this.taskContext = context;
		this.running = true;
	}

	@Override
	public int getNumberOfInputs() {
		return 1;
	}
	
	@Override
	public Class<AbstractRichFunction> getStubType() {
		return null;
	}

	@Override
	public int getNumberOfDriverComparators() {
		return 0;
	}

	@Override
	public void prepare() {
		ExecutionConfig executionConfig = taskContext.getExecutionConfig();
		this.objectReuseEnabled = executionConfig.isObjectReuseEnabled();

		if (LOG.isDebugEnabled()) {
			LOG.debug("NoOpDriver object reuse: " + (this.objectReuseEnabled ? "ENABLED" : "DISABLED") + ".");
		}
	}

	@Override
	public void run() throws Exception {
		// cache references on the stack
		final Counter numRecordsIn = this.taskContext.getMetricGroup().getIOMetricGroup().getNumRecordsInCounter();
		final Counter numRecordsOut = this.taskContext.getMetricGroup().getIOMetricGroup().getNumRecordsOutCounter();
		final MutableObjectIterator<T> input = this.taskContext.getInput(0);
		final Collector<T> output = new CountingCollector<>(this.taskContext.getOutputCollector(), numRecordsOut);

		//task2.3_iteration
		if (objectReuseEnabled) {
			T record = this.taskContext.<T>getInputSerializer(0).getSerializer().createInstance();

			while (this.running && ((record = input.next(record)) != null)) {
				numRecordsIn.inc();
				output.collect(record);
			}
		} else {
			T record;
			ExecutionConfig executionConfig = taskContext.getExecutionConfig();
			if(executionConfig.isEnableCompentionFunc()) { // 若使用补偿函数，则缓存中间记录数据
				List<T> tempData = new ArrayList<T>();
				numRecordsIn.inc(); // 记录迭代次数
				while(this.running && ((record = input.next()) != null)) {
					output.collect(record);
					tempData.add(record);
				}
				saveData = tempData;
				System.out.println("第" + numRecordsIn.getCount()  + "次迭代. --- 补偿.");
			} else {
				numRecordsIn.inc();
				while (this.running && ((record = input.next()) != null)) {
					output.collect(record);
				}
				System.out.println("第" + numRecordsIn.getCount()  + "次迭代. --- 非补偿");
			}
		}
		//end
	}
	
	@Override
	public void cleanup() {}

	@Override
	public void cancel() {
		this.running = false;
	}

	//task2.3_iteration
	public void writeInFile(String compensationDataPath) throws Exception {
		Path path = new Path(compensationDataPath + String.valueOf("/") + Thread.currentThread().hashCode() + String.valueOf(System.currentTimeMillis()) + ".txt"); //同一线程会写多次进行本地读写

		//本地读写需要保证每次文件名唯一，集群上效果不确定

		FileSystem fileSystem  = FileSystem.get(new URI(compensationDataPath));
		FSDataOutputStream os = null;
		os=fileSystem.create(path, FileSystem.WriteMode.OVERWRITE);

		List<T> data = saveData;
		for(T record : data) {

			byte[] buff = (record.toString()+"\n").getBytes();
			os.write(buff,0,buff.length);
		}
		os.close();
	}
	//end
}